package org.apache.ibatis.demo.proxy;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author：LJ
 * @Description：通用缓存装饰器示例
 * @Date: 2020/7/7
 * @Modified By:
 */
public class GenericCacheDecorator implements InvocationHandler {

  /**
   * 内存缓存
   */
  private Map<String, Object> cachedData = new HashMap<>();

  /**
   * 自定义空
   */
  private Object EMPTY = new Object();

  /**
   * 目标对象
   */
  private Object object;

  private GenericCacheDecorator(Object object) {
    this.object = object;

    try {
      //获取目标对象的所有属性、公开的方法和事件
      BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
      //循环获取目标对象的所有属性并映射为“EMPTY”
      for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
        cachedData.put(descriptor.getReadMethod().getName(), EMPTY);
      }
    } catch (IntrospectionException e) {
      e.printStackTrace();
    }
  }

  /**
   *
   * @param t 类型对象
   * @param interfaceClass 接口Class
   * @param <I> 接口
   * @param <T> 继承该接口的类
   * @return
   */
  public static <I, T extends I> I decorate(T t, Class<I> interfaceClass) {
    GenericCacheDecorator cacheDecorator = new GenericCacheDecorator(t);
    //生成I接口的代理对象,通过调用该方法的第三个参数{@link InvocationHandler#invoke}执行内部被代理的对象的对应方法
    return (I) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, cacheDecorator);
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //如果目标对象的方法以及有缓存值就直接返回；如果没有就执行目标对象的目标方法
    if (cachedData.containsKey(method.getName())) {
      Object o = cachedData.get(method.getName());
      if (o == EMPTY) {
        Object result = method.invoke(object, args);
        cachedData.put(method.getName(), result);
        return result;
      } else {
        return o;
      }
    }
    return method.invoke(object, args);
  }

  public static void main(String[] args) {
    MyObject object = new MyObject();
    IObject iObject = GenericCacheDecorator.decorate(object, IObject.class);
    System.out.println(iObject.getData());
    System.out.println(iObject.getData());
  }
}
